library ieee;
use ieee.std_logic_1164.all;

-- A more appropriate name for this block would be decode-stage-forwarder because it can be used
-- to forward the past values (further down the pipe) back to the decode stage to be used as
-- faux reg_rdat1,2

-- In this case it only handles the branching logic including address calculation and execution 
-- decision. It also forwards the R[Rs] value on a JumpReg


entity branchcontrol is
  port (
    dRs, dRt, exRd, memRd :     in std_logic_vector(4 downto 0);
    exRegWrite, memRegWrite :     in std_logic;
    dImmExtend, dPC4          :   in std_logic_vector(31 downto 0);
    Branch, BranchNE, JumpReg          :   in std_logic;
    REG_RDAT1, REG_RDAT2      :   in std_logic_vector(31 downto 0);
    exALU_out, memALU_out     :   in std_logic_vector(31 downto 0);
    
    BranchTaken               :   out std_logic;
    Branch_Addr               :   out std_logic_vector(31 downto 0)
  );
end branchcontrol;

architecture behavioral of branchcontrol is
  
  component addr
    port (A, B: in std_logic_vector(31 downto 0); CARRYIN : in std_logic; SUM : out std_logic_vector(31 downto 0); OVERFLOW: out std_logic);
  end component;
  
  
  signal EX_Hazard_A, EX_Hazard_B, MEM_Hazard_A, MEM_Hazard_B : std_logic;
  signal forwardA, forwardB : std_logic_vector(1 downto 0);
  signal Branch_B, fwd_rdat1, fwd_rdat2, Branch_Sum : std_logic_vector(31 downto 0);
  signal BraComp  : std_logic;

begin


 --that way its all contained in here instead of the mycpu file
 Branch_Addr <= fwd_rdat1 when JumpReg = '1' else
                Branch_Sum;
  
 Branch_B <= dImmExtend(29 downto 0) & "00";
 PC_BRANCH_ADD : addr PORT MAP (A => dPC4, B => Branch_B, carryin => '0', sum => Branch_Sum);
 BraComp <= '1' when (fwd_rdat1 xnor fwd_rdat2) = "11111111111111111111111111111111" else
            '0';
 BranchTaken <= ((Branch and BraComp) or (BranchNE and not BraComp)); 
   
--1. EX hazard
  EX_Hazard_A <= '1' when (Branch or BranchNE or JumpReg) = '1' and (exRegWrite = '1' and not ((exRd xnor "00000") = "11111") and (exRd xnor dRs) = "11111") else
                 '0';
     --ForwardA = 10

  EX_Hazard_B <= '1' when (Branch or BranchNE) = '1' and (exRegWrite = '1' and not ((exRd xnor "00000") = "11111") and (exRd xnor dRt) = "11111") else
                 '0';
      --ForwardB = 10
      
--2. MEM hazard

  MEM_Hazard_A <= '1' when (Branch or BranchNE or JumpReg) = '1' and (memRegWrite = '1' and not ((memRd xnor "00000") = "11111") and (memRd xnor dRs) = "11111") and EX_Hazard_A = '0' else
                  '0';
    --ForwardA = 01
    
  MEM_Hazard_B <= '1' when (Branch or BranchNE) = '1' and (memRegWrite = '1' and not ((memRd xnor "00000") = "11111") and (memRd xnor dRt) = "11111") and EX_Hazard_B = '0' else
                  '0';
    --ForwardB = 01
     
  forwardA <= EX_Hazard_A & MEM_Hazard_A;
  forwardB <= EX_Hazard_B & MEM_Hazard_B;
  
  --fwd_rdat1
    with forwardA select
      fwd_rdat1 <= REG_RDAT1  when  "00",
               exALU_out   when  "10",
               memALU_out     when  "01",
               x"BAD1BAD1"  when  others;
    
   --fwd_rdat2          
    with forwardB select
      fwd_rdat2 <=  REG_RDAT2  when  "00",
                     exALU_out   when  "10",
                     memALU_out     when  "01",
                     x"BAD2BAD2"  when  others;

    
end behavioral;
